package com.catazy.base.presentation.viewmodel

import timber.log.Timber
import kotlin.reflect.full.memberProperties

/**
 * 作者: Tao
 * 日期: 2024/12/16
 * 时间: 16:27
 * 描述: 用于调试应用程序的状态变化，特别是当需要记录和回溯某个视图（view）的状态时。
 *      通过传入 viewClassName，可以标识该调试器与哪个视图类相关联。
 */
class StateTimeTravelDebugger(private val viewClassName: String) {
    // 状态时间线
    private val stateTimeline = mutableListOf<StateTransition>()

    // 上一次动作
    private var lastViewAction: BaseAction<*>? = null

    /**
     * 添加动作
     */
    fun addAction(viewAction: BaseAction<*>) {
        lastViewAction = viewAction
    }

    /**
     * 添加状态转换
     */
    fun addStateTransition(oldState: BaseState, newState: BaseState) {
        val lastViewAction = checkNotNull(lastViewAction) {
            "lastViewAction is null. Please log action before logging state transition"
        }
        stateTimeline.add(StateTransition(oldState, lastViewAction, newState))
        this.lastViewAction = null
    }

    private fun getMessage() = getMessage(stateTimeline)

    private fun getMessage(stateTimeline: List<StateTransition>): String {
        if (stateTimeline.isEmpty()) {
            return "$viewClassName has no state transitions \n"
        }

        var message = ""

        stateTimeline.forEach { stateTransition ->
            message += "Action: $viewClassName.${stateTransition.action.javaClass.simpleName}:\n"

            propertyNames.forEach { propertyName ->
                val logLine = getLogLine(stateTransition.oldState, stateTransition.newState, propertyName)
                message += logLine
            }
        }

        return message
    }

    fun logAll() {
        Timber.d(getMessage())
    }

    fun logLast() {
        val states = listOf(stateTimeline.last())
        Timber.d(getMessage(states))
    }

    private fun getLogLine(oldState: BaseState, newState: BaseState, propertyName: String): String {
        val oldValue = getPropertyValue(oldState, propertyName)
        val newValue = getPropertyValue(newState, propertyName)
        val indent = "\t"

        return if (oldValue != newValue) {
            "$indent*$propertyName: $oldValue -> $newValue\n"
        } else {
            "$indent$propertyName: $newValue\n"
        }
    }

    // Get list of properties from  ViewState instances (all have the same type)
    private val propertyNames by lazy {
        stateTimeline.first().oldState.javaClass.kotlin.memberProperties.map { it.name }
    }

    private fun getPropertyValue(baseState: BaseState, propertyName: String): String {
        baseState::class.memberProperties.forEach {
            if (propertyName == it.name) {
                var value = it.getter.call(baseState).toString()

                if (value.isBlank()) {
                    value = "\"\""
                }

                return value
            }
        }
        return ""
    }

    /**
     * 状态转换类
     */
    private data class StateTransition(
        // 旧状态
        val oldState: BaseState,
        // 动作
        val action: BaseAction<*>,
        // 新状态
        val newState: BaseState,
    )
}